/*
 * Copyright the original author or authors.
 * 
 * Licensed under the MOZILLA PUBLIC LICENSE, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.mozilla.org/MPL/MPL-1.1.html
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
 
import com.bourre.log.PixlibStringifier;
import com.bourre.structures.Range;
import com.kairos.engine.RTEvent;
import com.kairos.utils.monitor.MonitorField;

/**
 * Abstract class for all objects used as field by the {@code Monitor}.
 * 
 * <p>Extends that class and override the {@code registerValue} method
 * to create your own behavior. See {@code TimeField} or {@code FPSField}
 * for an example of concret statistic field.</p>
 * 
 * Create your own monitor field by extending {@code AbstractMonitorField}
 * <code>
 * import com.bourre.structures.Range;
 * import com.kairos.engine.RTEvent;
 * import com.kairos.utils.monitor.AbstractMonitorField;
 * import com.kairos.utils.monitor.MonitorField;
 * 
 * class myPackage.MyField extends AbstractMonitorField implements MonitorField
 * {
 * 	public function MyField()
 * 	{
 * 		super( "My Field Label" , 0, "ms", 0xffffffff, new Range ( 0, 50 ) );
 * 	}
 * 	// Simply override the registerValue method to activate the field.
 * 	public function registerValue ( e : RTEvent ) : Void
 * 	{
 * 		_nLastValue = _nValue;
 * 		_nValue = AnyObject.count;
 * 	}
 * }
 * </code>
 * 
 * If you don't want to display your field as a curve of the graph, simply override
 * the {@code isCurve} method and return {@code false}. 
 * 
 * <code>
 * import com.bourre.structures.Range;
 * import com.kairos.engine.RTEvent;
 * import com.kairos.utils.monitor.AbstractMonitorField;
 * import com.kairos.utils.monitor.MonitorField;
 * 
 * class myPackage.MyField extends AbstractMonitorField implements MonitorField
 * {
 * 	public function MyField()
 * 	{
 * 		super( "My Field Label" , 0, "ms", 0xffffffff, new Range ( 0, 50 ) );
 * 	}
 * 	// Simply override the registerValue method to activate the field.
 * 	public function registerValue ( e : RTEvent ) : Void
 * 	{
 * 		_nLastValue = _nValue;
 * 		_nValue = AnyObject.count;
 * 	}
 * 	// The field is not drawn as a curve in the graph.
 * 	public function isCurve () : Boolean
 * 	{
 * 		return false;
 * 	}
 * }</code>
 * 
 * @author 	Cédric Néhémie
 * @see		MonitorField
 * @see		Monitor
 */
class com.kairos.utils.monitor.AbstractMonitorField implements MonitorField
{
	private var _sName : String;
	private var _sUnit : String;
	private var _nColor : Number;
	private var _nValue : Number;
	private var _nLastValue : Number;
	private var _nHeight : Number;
	private var _rDisplayRange : Range;
	private var _bEnabled : Boolean;
	
	/**
	 * Call the super constructor to defines common properties of the field.
	 * 
	 * @param name	{@code String} name of the field.
	 * @param val	{@code Number} start value of the field.
	 * @param unit	{@code String} of the unit for the field.
	 * @param col	{@code Number} color of the field.
	 * @param dr	{@code Range} of displaed values.
	 */
	private function AbstractMonitorField ( name : String,  val : Number, unit : String, col:Number, r : Range )
	{
		_sName = name;
		_nLastValue = _nValue = val;
		_sUnit = unit;
		_nColor = col;
		_rDisplayRange = r;
		_bEnabled = true;
	}
	
	/**
	 * Returns the current value of the field formatted as a {@code String}.
	 * 
	 * <p>If the value have a specific unit, the formatted string
	 * will include it.</p>
	 * 
	 * @return {@code String} of the formatted value.
	 */
	public function getFormattedValue():String
	{
		return _nValue + " " + _sUnit;
	}
	
	/**
	 * Returns the range between the last value and the current value
	 * of the field in the display bitmap coordinates.
	 * 
	 * <p>The range contained values in pixels according to the
	 * graph's bitmap height passed to this object when added
	 * to the {@code Monitor}.</p>
	 * 
	 * @return {@code Range} of value variations.
	 */
	public function getDifference():Range
	{
		return new Range ( _getDisplayValue( _nLastValue ), _getDisplayValue( _nValue ) );;
	}
	/**
	 * Returns the color that the {@code Monitor} will use to create
	 * the field label and the field curve.
	 * 
	 * <p>Colors are described with ARGB hexadecimal value such
	 * {@code 0xaarrggbb}.</p>
	 * 
	 * @return 	{@code Number} color of the field.
	 */
	public function getColor():Number
	{
		return _nColor;
	}
	/**
	 * Defines the color of the current field.
	 * 
	 * <p>The color have to be defined before its add to the {@code Monitor}
	 * otherwise the modification isn't considered.</p>
	 * 
	 * <p>Colors are described with ARGB hexadecimal value such
	 * {@code 0xaarrggbb}.</p>
	 * 
	 * @param	col	{@code Number} color for the field.
	 */
	public function setColor( col  : Number ) : Void
	{
		_nColor = col;
	}
	/**
	 * Defines the display values range for the field. All values out
	 * of that range isn't displayed in the graph.</p>
	 * 
	 * <p>That range is used to compute the {@code getDifference}
	 * return value.</p>
	 * 
	 * @param	r	{@code Range} of displayed values.
	 */
	public function setDisplayRange( r : Range ) : Void
	{
		_rDisplayRange = r;
	}
	/**
	 * Defines the height of the display area. That value is set
	 * by the {@code Monitor} when the object is added to it.
	 * 
	 * <p>That value is used to compute the {@code getDifference}
	 * return value.</p>
	 * 
	 * @param	n	{@code Number} of the graph's bitmap height.
	 */
	public function setDisplayHeight( n:Number ) : Void
	{
		_nHeight = n;
	}
	/**
	 * Called by the {@code Monitor} on each frame of the
	 * animation.
	 * 
	 * <p>In that method the object set the last value and register
	 * its current value from its source.</p>
	 * 
	 * <p>Its important to also register the last value in the same time,
	 * if ommitted the monitor cannot draw the line, it can only draw
	 * a single pixel for the sole value.</p>
	 * 
	 * <p>Overrides that method to create your own monitor field.</p>
	 * 
	 * @param 	e	The RTEvent dispatched by the {@code RTBeacon}.
	 */
	public function registerValue ( e : RTEvent ) : Void {}
	
	/**
	 * Returns the value in graph coordinates according to the display range
	 * and the display height.
	 * 
	 * @param	v	{@code Number} value of the field
	 * @return 	{@code Number} value in display bitmap coordinates.
	 */
	private function _getDisplayValue ( v : Number ) : Number
	{
		return _nHeight - Math.floor( ( ( v - _rDisplayRange.min ) / _rDisplayRange.max ) * _nHeight );
	}
	/**
	 * Returns the name of the current field witch the {@code Monitor}
	 * will use as label for the field.
	 * 
	 * <p>As in general we used to display the smallest monitor as possible, 
	 * try to set a small and explicit label. The monitor will allways adjust
	 * the size of the monitor on the bitmap rather than on the labels so
	 * textfields will crop the text if it's longer than the allowed size.</p>
	 * 
	 * @return {@code String} name of the field
	 */
	public function getName() : String 
	{
		return _sName;
	}
	/**
	 * Returns {@code true} if the field can be displayed as curve
	 * in the graph, {@code false} otherwise.
	 * 
	 * @return {@code true} if the field is a curve in the graph.
	 */
	public function isCurve() : Boolean
	{
		return true;
	}
	/**
	 * Returns {@code true} if the field can be displayed as label
	 * in the monitor, {@code false} otherwise.
	 * 
	 * @return {@code true} if the field have a label in the monitor.
	 */
	public function isLabel() : Boolean
	{
		return true;
	}
	
	/**
	 * Indicates if field must be displayed ( calculated ) in the graph 
	 * view.
	 * 
	 * @param b {@code true} to display the field in the graph.
	 */
	public function setEnabled( b : Boolean ) : Void
	{
		_bEnabled = b;
	}
	
	/**
	 * Returns {@code true} if the field is enabled ( can be displayed in 
	 * the graph ).
	 * 
	 * @return {@code true} if the field can be drawn in the graph.
	 */
	public function isEnabled(  ) : Boolean
	{
		return _bEnabled;
	}
	
	/**
	 * Returns the {@code String} representation of the object.
	 * 
	 * @return {@code String} representation of the object.
	 */
	public function toString() : String 
	{
		return PixlibStringifier.stringify( this );
	}
}